<script lang="ts">
    import { Checkbox } from "../Checkbox";
    import Icon from "../Common/Icons/Icon.svelte";
    import Loading from "../Common/Loading/Loading.svelte";
    import { useStyle } from "../utils/useStyle";
    import type { TreeContextProps, TreeNode, TreeNodeProps } from "./Tree";
    import { dragHoverPartEnum, useTreeContext } from "./Tree.svelte";

    const { data, ...props }: TreeNodeProps = $props();
    const ctx: TreeContextProps = useTreeContext();
    const keyField = ctx.store.keyField;
    let nodeBody: any;
    let dragoverBefore = $state(false);
    let dragoverBody = $state(false);
    let dragoverAfter = $state(false);

    const opened = $derived(ctx.store.expandedKeys?.includes(data[keyField]));
    const selected = $derived(ctx.store.selectedKey === data[keyField]);
    let dragging = $state(false);

    const classList = $derived({
        "cm-tree-item": true,
        "cm-tree-item-open": opened,
        "cm-tree-item-selected": selected,
        "cm-tree-item-dragging": dragging,
        [`${ctx.draggingClass}`]: !!ctx.draggingClass && dragging,
        [`${ctx.selectedClass}`]: ctx.selectedClass && selected,
    });

    const hasChildren = $derived((data.children && data.children.length) || data.loading);

    const onContextMenu = () => {
        if (ctx && ctx.contextMenu) {
            ctx.onContextMenu?.({ ...data });
        }
    };

    const onOpenClose = () => {
        ctx.onOpenClose?.(data);
    };

    const onNodeSelect = () => {
        ctx.onNodeSelect?.(data);
    };

    const onDragStart = (e: any) => {
        if (data.disabled) {
            return;
        }
        if (ctx.draggable) {
            if (e.dataTransfer) {
                e.dataTransfer.setData("node", data[ctx.store.keyField]);
            }
            // 打开的父节点进行关闭
            if (opened) {
                ctx.onOpenClose(data);
            }
            dragging = true;
            ctx.onDragStart?.(e, data);
        }
    };

    const onDragEnd = (e: any) => {
        if (ctx?.store) {
            dragging = false;
        }
    };

    const getHoverPart = (e: any) => {
        const boundingClientRect = nodeBody.getBoundingClientRect();
        const height = boundingClientRect.height;
        const y = e.clientY - boundingClientRect.top;
        if (y <= height * 0.3) return dragHoverPartEnum.before;
        if (y <= height * (0.3 + 0.4)) return dragHoverPartEnum.body;
        return dragHoverPartEnum.after;
    };

    const onDragEnter = (e: any) => {
        e.preventDefault();
        const hoverPart = getHoverPart(e);
        resetDragoverFlags(hoverPart);
        ctx.onDragEnter?.(e, data, hoverPart);
    };

    const onDragOver = (e: any) => {
        e.preventDefault();
        const hoverPart = getHoverPart(e);
        resetDragoverFlags(hoverPart);
        ctx.onDragOver?.(e, data, hoverPart);
    };

    const onDragLeave = (e: any) => {
        const hoverPart = getHoverPart(e);
        resetDragoverFlags(hoverPart, true);
        ctx.onDragLeave?.(e, data, hoverPart);
    };

    const onDrop = (e: any) => {
        const hoverPart = getHoverPart(e);
        resetDragoverFlags(hoverPart, true);
        ctx.onDrop?.(e, data[ctx.store.keyField], hoverPart);
    };

    const resetDragoverFlags = (hoverPart: dragHoverPartEnum, isLeaveOrDrop = false) => {
        dragoverBefore = false;
        dragoverBody = false;
        dragoverAfter = false;
        if (!isLeaveOrDrop) {
            if (hoverPart === dragHoverPartEnum.before) {
                dragoverBefore = true;
            } else if (hoverPart === dragHoverPartEnum.body) {
                dragoverBody = true;
            } else if (hoverPart === dragHoverPartEnum.after) {
                if (isLast()) {
                    dragoverAfter = true;
                }
            }
        }
    };

    const isLast = () => {
        const parent = data._parent;
        if (parent) {
            const index = parent.children?.findIndex((item: TreeNode) => item[ctx.store.keyField] === data[ctx.store.keyField]);
            if (parent.children && index === parent.children.length - 1) return true;
        }
        return false;
    };

    const paddingStyle = $derived(useStyle({}, { "padding-left": 16 * data._level! + "px", height: "100%" }));
    const beforeClassList = $derived({ "cm-tree-node-drop": true, "cm-tree-node-drop_active": dragoverBefore });
    const afterClassList = $derived({ "cm-tree-node-drop": true, "cm-tree-node-drop_active": dragoverAfter });
    const bodyClassList = $derived({ "cm-tree-node-content": true, "cm-tree-node-drop_active": dragoverBody, [`${ctx.dragHoverClass}`]: !!ctx.dragHoverClass && dragoverBody });

    const loading = $derived(ctx.store.store.nodeMap[data[ctx.store.keyField]]?.__loading);
</script>

{#snippet renderIcon()}
    {#if ctx.directory}
        {#if ctx.customIcon && typeof ctx.customIcon === "function"}
            <span class="cm-tree-item-icon">{@render ctx.customIcon(data)}</span>
        {:else if data.icon}
            <span class="cm-tree-item-icon">{@render data.icon?.()}</span>
        {:else if hasChildren}
            <span class="cm-tree-item-folder"></span>
        {:else}
            <span class="cm-tree-item-file"></span>
        {/if}
    {/if}
{/snippet}

<!-- svelte-ignore a11y_no_static_element_interactions -->
<div {...props} class={classList} style={paddingStyle} ondragenter={onDragEnter} ondragover={onDragOver} ondragleave={onDragLeave} ondrop={onDrop} bind:this={nodeBody}>
    <div class={beforeClassList}></div>
    <div class={bodyClassList} ondragstart={onDragStart} ondragend={onDragEnd} draggable={ctx.draggable && !data.disabled}>
        {#if loading}
            <Loading color={"#1890ff"} size={16} />
        {:else}
            <!-- svelte-ignore a11y_click_events_have_key_events -->
            <span class={"cm-tree-arrow " + (hasChildren ? "" : "hide")} onclick={onOpenClose}>
                {#if ctx.arrowIcon && typeof ctx.arrowIcon}
                    {@render ctx.arrowIcon?.()}
                {:else}
                    <Icon icon="chevronRight" />
                {/if}
            </span>
        {/if}
        {#if ctx.checkable}
            <Checkbox
                disabled={ctx.store.store.nodeMap[data[ctx.store.keyField]].disabled}
                checked={ctx.store.store.nodeMap[data[ctx.store.keyField]].checked}
                onchange={(checked) => ctx.onNodeCheck(data, checked)}
            />
        {/if}
        {@render renderIcon()}
        <!-- svelte-ignore a11y_click_events_have_key_events -->
        <span oncontextmenu={onContextMenu} class={`cm-tree-title`} onclick={onNodeSelect}>
            <span class="cm-tree-text">{data.title}</span>
            {#if data.patch}
                <span class="cm-tree-patch">{data.patch}</span>
            {/if}
        </span>
    </div>
    <div class={afterClassList}></div>
</div>
